// Fill out your copyright notice in the Description page of Project Settings.


#include "AbilitySystem/ExecCalc/ExecCalc_Damage.h"

#include "AbilitySystem/AuraAbilitySystemBPLibrary.h"
#include "AbilitySystem/AuraAttributeSet.h"
#include "Game/AuraGameplayTags.h"
#include "Interaction/CombatInterface.h"

/** 这里结构体不加F是因为它是内部结构体，不需要外部获取，也不需要在蓝图中使用 */

// 定义属性捕获
struct SDamageStatics
{
	// 声明属性的捕获定义
	DECLARE_ATTRIBUTE_CAPTUREDEF(Armor) // 展开后会创建 FGameplayEffectAttributeCaptureDefinition ArmorDef
	DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetration)
	DECLARE_ATTRIBUTE_CAPTUREDEF(BlockChance)
	DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalHitChance)
	DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalHitDamage)
	DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalHitResistance)
	DECLARE_ATTRIBUTE_CAPTUREDEF(FireResistance)
	DECLARE_ATTRIBUTE_CAPTUREDEF(LightningResistance)
	DECLARE_ATTRIBUTE_CAPTUREDEF(ArcaneResistance)
	DECLARE_ATTRIBUTE_CAPTUREDEF(PhysicalResistance)


	// 构造函数中初始化捕获定义
	SDamageStatics()
	{
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, CriticalHitChance, Source, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, CriticalHitDamage, Source, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, CriticalHitResistance, Target, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, Armor, Target, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, BlockChance, Target, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, ArmorPenetration, Source, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, FireResistance, Target, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, LightningResistance, Target, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, ArcaneResistance, Target, false);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, PhysicalResistance, Target, false);
	}
};

// 单例访问器
// 静态文件函数
static const SDamageStatics& DamageStatics()
{
	// 静态局部变量，确保只创建一次
	static SDamageStatics DamageStatics;

	return DamageStatics;
}

// 在构造中添加要捕获的相关属性
UExecCalc_Damage::UExecCalc_Damage()
{
	RelevantAttributesToCapture.Add(DamageStatics().ArmorDef);
	RelevantAttributesToCapture.Add(DamageStatics().BlockChanceDef);
	RelevantAttributesToCapture.Add(DamageStatics().ArmorPenetrationDef);
	RelevantAttributesToCapture.Add(DamageStatics().CriticalHitDamageDef);
	RelevantAttributesToCapture.Add(DamageStatics().CriticalHitResistanceDef);
	RelevantAttributesToCapture.Add(DamageStatics().CriticalHitChanceDef);
	RelevantAttributesToCapture.Add(DamageStatics().FireResistanceDef);
	RelevantAttributesToCapture.Add(DamageStatics().LightningResistanceDef);
	RelevantAttributesToCapture.Add(DamageStatics().ArcaneResistanceDef);
	RelevantAttributesToCapture.Add(DamageStatics().PhysicalResistanceDef);
}

void UExecCalc_Damage::Execute_Implementation(
	const FGameplayEffectCustomExecutionParameters& ExecutionParams,
	FGameplayEffectCustomExecutionOutput& OutExecutionOutput
) const
{
	// 获取ASC
	const UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent();
	const UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();

	//获取AvatarActor
	AActor* SourceAvatarActor = SourceASC ? SourceASC->GetAvatarActor() : nullptr;
	AActor* TargetAvatarActor = TargetASC ? TargetASC->GetAvatarActor() : nullptr;

	// AvatarActor转为战斗接口
	ICombatInterface* SourceCombatInterface = Cast<ICombatInterface>(SourceAvatarActor);
	ICombatInterface* TargetCombatInterface = Cast<ICombatInterface>(TargetAvatarActor);

	//获取挂载此类的GE实例
	const FGameplayEffectSpec EffectSpec = ExecutionParams.GetOwningSpec();

	// 获取ContextHandle
	FGameplayEffectContextHandle EffectContextHandle = EffectSpec.GetContext();

	// 获取源和目标的游戏标签
	const FGameplayTagContainer* SourceTags = EffectSpec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = EffectSpec.CapturedTargetTags.GetAggregatedTags();

	//设置评估参数
	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;

	// ----------------------------------------------------------------------------------------------------------------
	//	获取应用所有修改器效果的最终属性值
	// ----------------------------------------------------------------------------------------------------------------

	// 获取目标护甲
	float TargetArmor = 0.f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
		DamageStatics().ArmorDef,
		EvaluationParameters,
		TargetArmor
	);
	TargetArmor = FMath::Max(TargetArmor, 0.f);

	// 获取目标格挡
	float TargetBlockChance = 0.f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
		DamageStatics().BlockChanceDef,
		EvaluationParameters,
		TargetBlockChance
	);
	TargetBlockChance = FMath::Max(TargetBlockChance, 0.f);

	// 获取源护甲穿透
	float SourceArmorPenetration = 0.f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
		DamageStatics().ArmorPenetrationDef,
		EvaluationParameters,
		SourceArmorPenetration
	);
	TargetBlockChance = FMath::Max(SourceArmorPenetration, 0.f);

	// 获取源暴击伤害
	float SourceCriticalHitChance = 0.f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
		DamageStatics().CriticalHitChanceDef,
		EvaluationParameters,
		SourceCriticalHitChance
	);
	SourceCriticalHitChance = FMath::Max(SourceCriticalHitChance, 0.f);

	// 获取源暴击伤害
	float SourceCriticalHitDamage = 0.f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
		DamageStatics().CriticalHitDamageDef,
		EvaluationParameters,
		SourceCriticalHitDamage
	);
	SourceCriticalHitDamage = FMath::Max(SourceCriticalHitDamage, 0.f);

	// 获取目标暴击抗性
	float TargetCriticalHitResistance = 0.f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
		DamageStatics().CriticalHitResistanceDef,
		EvaluationParameters,
		TargetCriticalHitResistance
	);
	TargetCriticalHitResistance = FMath::Max(TargetCriticalHitResistance, 0.f);
	
	// ----------------------------------------------------------------------------------------------------------------
	//	计算属性伤害
	// ----------------------------------------------------------------------------------------------------------------

	// 伤害 TODO: 优化计算伤害
	float Damage = 0.f;

	FAuraGameplayTags GameplayTags = FAuraGameplayTags::Get();
	//存储标签和属性捕捉定义的Map
	TMap<FGameplayTag, FGameplayEffectAttributeCaptureDefinition> TagsToCaptureDefs;
	//添加标签和属性捕捉定义的数据
	TagsToCaptureDefs.Add(GameplayTags.Attributes_Resistance_Fire, DamageStatics().FireResistanceDef);
	TagsToCaptureDefs.Add(GameplayTags.Attributes_Resistance_Lightning, DamageStatics().LightningResistanceDef);
	TagsToCaptureDefs.Add(GameplayTags.Attributes_Resistance_Arcane, DamageStatics().ArcaneResistanceDef);
	TagsToCaptureDefs.Add(GameplayTags.Attributes_Resistance_Physical, DamageStatics().PhysicalResistanceDef);
	for (const TTuple<FGameplayTag, FGameplayTag>& Pair : GameplayTags.DamageTypesToResistanceMap)
	{
		const FGameplayTag DamageType = Pair.Key;
		const FGameplayTag ResistanceType = Pair.Value;
		//检查对应的捕捉定义是否设置，防止报错
		checkf(
			TagsToCaptureDefs.Contains(ResistanceType),
			TEXT("在ExecCalc_Damage中，无法获取到Tag[%s]对应的属性快照"),
			*ResistanceType.ToString()
		);
		//通过抗性标签获取到捕捉定义
		const FGameplayEffectAttributeCaptureDefinition CaptureDef = TagsToCaptureDefs[ResistanceType];

		//获取抗性值
		float Resistance = 0.f;
		ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(CaptureDef, EvaluationParameters, Resistance);
		Resistance = FMath::Clamp(Resistance, 0.f, 100.f); //将抗住限制在0到100

		//通过Tag获取对应伤害类型的值，如果没设置SetByCaller将获取0
		float DamageTypeValue = EffectSpec.GetSetByCallerMagnitude(DamageType,false);

		//通过抗性计算出能够对角色造成的伤害值
		DamageTypeValue *= (100.f - Resistance) / 100.f;
		//将每种属性伤害值合并进行后续计算
		Damage += DamageTypeValue;
	}

	// ----------------------------------------------------------------------------------------------------------------
	//	计算伤害减免
	// ----------------------------------------------------------------------------------------------------------------

	// 从护甲穿透曲线获取护甲穿透系数
	const UCharacterClassInfo* ClassInfo = UAuraAbilitySystemBPLibrary::GetCharacterClassDefaultInfo(SourceAvatarActor);
	const FRealCurve* ArmorPenetrationCurve = ClassInfo->DamageCalculationCoefficients->FindCurve(
		"ArmorPenetration",
		FString()
	);
	const float ArmorPenetrationCoefficient = ArmorPenetrationCurve->Eval(SourceCombatInterface->GetLevel());

	// 有效护甲.
	// 护甲穿透将按照比例忽略目标的护甲值，
	// 护甲穿透的值可以根据某个方程计算出实际护甲穿透率（可以根据等级，等级越高，每一点护甲穿透值的比例越低）
	const float EffectiveArmor = TargetArmor * (100.f - SourceArmorPenetration * ArmorPenetrationCoefficient) / 100.f;

	// 获取有效护甲系数
	const FRealCurve* ArmorCurve = ClassInfo->DamageCalculationCoefficients->FindCurve("Armor", FString());
	const float EffectiveArmorCoefficient = ArmorCurve->Eval(TargetCombatInterface->GetLevel());

	// 模拟护甲伤害减免
	Damage *= (100.f - EffectiveArmor * EffectiveArmorCoefficient) / 100.f;

	// 模拟格挡
	if (TargetBlockChance > FMath::RandRange(1, 100))
	{
		// 设置上下文格挡标志位
		UAuraAbilitySystemBPLibrary::SetIsBlockedHit(EffectContextHandle, true);
		Damage *= 0.5f;
	}

	// ----------------------------------------------------------------------------------------------------------------
	//	暴击伤害计算
	// ----------------------------------------------------------------------------------------------------------------

	// 从表获取暴击抵抗系数
	const FRealCurve* CriticalHitResistanceCurve = ClassInfo->DamageCalculationCoefficients->FindCurve(
		"CriticalHitResistance",
		FString()
	);
	const float CriticalHitResistanceCoefficient = CriticalHitResistanceCurve->Eval(TargetCombatInterface->GetLevel());

	// 计算是否暴击
	const float EffectiveCriticalHitChance = SourceCriticalHitChance - TargetCriticalHitResistance *
		CriticalHitResistanceCoefficient;
	if (EffectiveCriticalHitChance > FMath::RandRange(1, 100))
	{
		// 设置上下文暴击标志位
		UAuraAbilitySystemBPLibrary::SetIsCriticalHit(EffectContextHandle, true);
		// 暴击伤害
		Damage = SourceCriticalHitDamage + Damage * 2;
	}


	//============================================================================================================//


	// 创建修改器评估数据
	const FGameplayModifierEvaluatedData DamageEvaluatedData(
		UAuraAttributeSet::GetIncomingDamageAttribute(),
		EGameplayModOp::Additive,
		Damage
	);

	//添加输出修改器
	OutExecutionOutput.AddOutputModifier(DamageEvaluatedData);
}
